<?php
namespace aop\config;

trait MonitorOperationTrait {

    /**
     * 解析参数规则
     * @param  [array]  $params  [格式如下]
     * array(
     *       'execute_flag' => 'before',     //方法执行顺序
     *       'class_name' => 'aop\example\ExampleOneClass', //被绑定于容器中的类的命令空间名称
     *       'method_name' => 'returnAny',  //被绑定于容器中的方法的名称
     *       'method_param' => [],          //被绑定于容器中的方法所需的参数列表
     *       'param_rule' => '',            //注释解析规则(本例中规则为形似param[type&num]|return[object&success])
     *       'param_name_list' => [],       ///被绑定于容器中的方法所需参数对应的名称列表
     *       'method_result' => NULL        //被绑定于容器中的方法执行后的返回结果
     * )
     * param_rule 规则也可以自定义
     *
     * @return [array]
     */
    final protected function _monitorExampleCenter($params) {
        $executeFlag = isset($params['execute_flag']) ? $params['execute_flag'] : '';
        $className = isset($params['class_name']) ? $params['class_name'] : '';
        $methodName = isset($params['method_name']) ? $params['method_name'] : '';
        if (!$executeFlag || !$className || !$methodName) {
            return [];
        }

        return array_merge([
            'execute_flag' => $executeFlag,
            'class_name' => $className,
            'method_name' => $methodName
            ], $this->_interpretParamRule($params));
    }

    /**
     * 解析参数规则
     * @param  [array]  $params  [格式如下]
     * array(
     *       'execute_flag' => 'before',     //方法执行顺序
     *       'class_name' => 'aop\example\ExampleOneClass', //被绑定于容器中的类的命令空间名称
     *       'method_name' => 'returnAny',  //被绑定于容器中的方法的名称
     *       'method_param' => [],          //被绑定于容器中的方法所需的参数列表
     *       'param_rule' => '',            //注释解析规则(本例中规则为形似param[type&num]|return[object&success])
     *       'param_name_list' => [],       ///被绑定于容器中的方法所需参数对应的名称列表
     *       'method_result' => NULL        //被绑定于容器中的方法执行后的返回结果
     * )
     * param_rule 规则也可以自定义
     *
     * @return [array]
     */
    final protected function _interpretParamRule($params) {
        $paramRule = isset($params['param_rule']) ? $params['param_rule'] : '';
        if (!$paramRule) {
            return [];
        }

        return array_merge($this->_interpretParam($params, $paramRule), $this->_interpretReturn($params, $paramRule));
    }

    final private function _interpretParam($params, $paramRule) {
        $interpretedParams = [];
        $methodParams = isset($params['method_param']) ? $params['method_param'] : [];
        $paramNameList = isset($params['param_name_list']) ? $params['param_name_list'] : [];

        if (!$paramNameList || !$methodParams) {
            return $interpretedParams;
        }

        $recordParams = $this->_substrParamRule($paramRule, 'param[');
        if (!$recordParams) {
            return $interpretedParams;
        }

        foreach ($recordParams as $_paramName) {
            $_index = array_search($_paramName, $paramNameList, TRUE);
            if ($_index === FALSE) {
                continue;
            }

            isset($methodParams[$_index]) && $interpretedParams[$_paramName] = $methodParams[$_index];
        }

        return $interpretedParams;
    }

    final private function _interpretReturn($params, $paramRule) {
        $interpretedReturn = [];
        $methodResult = isset($params['method_result']) ? $params['method_result'] : NULL;

        if (is_null($methodResult)) {
            return $interpretedReturn;
        }

        $recordParams = $this->_substrParamRule($paramRule, 'return[');
        if (!$recordParams) {
            return $interpretedReturn;
        }

        $firstRule = array_shift($recordParams);
        if (gettype($methodResult) !== $firstRule) {
            return $interpretedReturn;
        }

        if ($firstRule === 'object' && $recordParams) {
            foreach ($recordParams as $_key) {
                if (!isset($methodResult->$_key)) {
                    continue;
                }
                $_tmpResult = $this->_convertBaseType(gettype($methodResult->$_key), $methodResult->$_key);
                is_null($methodResult) || $interpretedReturn['return:' . $_key] = $_tmpResult;
            }
        } elseif ($firstRule === 'array' && $recordParams) {
            foreach ($recordParams as $_key) {
                if (!isset($methodResult[$_key])) {
                    continue;
                }
                $_tmpResult = $this->_convertBaseType(gettype($methodResult[$_key]), $methodResult[$_key]);
                is_null($methodResult) || $interpretedReturn['return:' . $_key] = $_tmpResult;
            }
        } else {
            $methodResult = $this->_convertBaseType($firstRule, $methodResult);
            is_null($methodResult) || $interpretedReturn['return:result'] = $methodResult;
        }

        return $interpretedReturn;
    }

    final private function _convertBaseType($valType, $value) {
        switch ($valType) {
            case 'string':
            case 'integer':
                return $value;

            case 'boolean':
                return $value ? 1 : 0;

            default:
                return NULL;
        }
    }

    final private function _substrParamRule($paramRule, $paramFlag = '') {
        if (!$paramFlag) {
            return [];
        }

        $flagPos = strpos($paramRule, $paramFlag);
        if ($flagPos === FALSE) {
            return [];
        }

        $ruleStartPos = $flagPos + strlen($paramFlag);
        $ruleEndPos = strpos($paramRule, ']', $ruleStartPos);
        if ($ruleEndPos === FALSE) {
            return [];
        }

        return explode('&', substr($paramRule, $ruleStartPos, $ruleEndPos - $ruleStartPos));
    }
}